<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
    <style>
        * {
            padding: 0;
            margin: 0;
            margin: 0;
        }

        .container {
            overflow: auto;
            display: flex;
        }
    </style>
</head>
<div id="app">
    <div class="container" ref="container" :style="{ height: containerHeight }">
        <div class="empty" :style="{ height: emptyHeight }"></div>
        <ul class="list" :style="{ transform: `translateY(${translateY})` }">
            <li v-for="item in listData" :key="item" class="item" :style="{ height: itemHeight }">{{ item }}</li>
        </ul>
    </div>
</div>
<body>
    <script src="https://unpkg.com/vue@next"></script>
    <script>
        const { ref, onMounted, computed } = Vue
        const App = {
            setup() {
                const oriData = Array.from({ length: 1000 }, (v, k) => k)
                const itemHeight = 20
                const emptyHeight = itemHeight * oriData.length
                const containerHeight = window.innerHeight
                const itemCount = Math.ceil(containerHeight / itemHeight)
                const container = ref(null)
                const start = ref(0)
                const translateY = ref(0)
                const listData = computed(() => {
                    return oriData.slice(start.value, start.value + itemCount + 1)
                })
                onMounted(() => {
                    container.value.addEventListener('scroll', e => {
                        const { scrollTop } = e.target
                        start.value = Math.floor(scrollTop / itemHeight)
                        translateY.value = scrollTop + 'px'
                    })
                })
                return {
                    listData,
                    container,
                    translateY,
                    containerHeight: containerHeight + 'px',
                    itemHeight: itemHeight + 'px',
                    emptyHeight: emptyHeight + 'px'
                }
            }
        }
        Vue.createApp(App).mount('#app')
    </script>
</body>
</html>